/**
 * URL 工具函数 - JavaScript 版本
 * 提供 URL 验证、解析、提取、格式化等功能
 * 支持直接复制粘贴使用，无需额外配置
 * 每个函数都是独立的，可以单独复制使用
 */

// 常量定义
const PROTOCOLS = {
  HTTP: 'http:',
  HTTPS: 'https:',
  FTP: 'ftp:',
  FILE: 'file:',
  WS: 'ws:',
  WSS: 'wss:'
};

const PORTS = {
  HTTP: 80,
  HTTPS: 443,
  FTP: 21,
  SSH: 22,
  SMTP: 25,
  POP3: 110,
  IMAP: 143,
  MYSQL: 3306,
  POSTGRES: 5432,
  REDIS: 6379,
  MONGODB: 27017
};

/**
 * 验证 URL 是否有效
 * @param {string} url - 要验证的 URL
 * @param {Object} options - 验证选项
 * @returns {boolean} 是否有效
 */
function isValidUrl(url, options = {}) {
  if (!url || typeof url !== 'string') {
    return false;
  }

  const {
    protocols = [PROTOCOLS.HTTP, PROTOCOLS.HTTPS],
    requireProtocol = true,
    allowLocalhost = true,
    allowIP = true
  } = options;

  try {
    const urlObj = new URL(url);
    
    // 检查协议
    if (requireProtocol && !protocols.includes(urlObj.protocol)) {
      return false;
    }

    // 检查主机名
    const hostname = urlObj.hostname;
    
    // 不允许 localhost
    if (!allowLocalhost && (hostname === 'localhost' || hostname === '127.0.0.1')) {
      return false;
    }

    // 不允许 IP 地址
    if (!allowIP && isIPAddress(hostname)) {
      return false;
    }

    return true;
  } catch {
    return false;
  }
}

/**
 * 解析 URL
 * @param {string} url - 要解析的 URL
 * @returns {Object} 解析结果
 */
function parseUrl(url) {
  try {
    const urlObj = new URL(url);
    
    return {
      isValid: true,
      url: {
        protocol: urlObj.protocol,
        hostname: urlObj.hostname,
        port: urlObj.port,
        pathname: urlObj.pathname,
        search: urlObj.search,
        hash: urlObj.hash,
        origin: urlObj.origin,
        href: urlObj.href
      }
    };
  } catch (error) {
    return {
      isValid: false,
      error: error instanceof Error ? error.message : 'Invalid URL'
    };
  }
}

/**
 * 从文本中提取 URL
 * @param {string} text - 包含 URL 的文本
 * @param {Object} options - 提取选项
 * @returns {Object} 提取结果
 */
function extractUrls(text, options = {}) {
  if (!text || typeof text !== 'string') {
    return {
      urls: [],
      uniqueUrls: [],
      count: 0,
      uniqueCount: 0
    };
  }

  const {
    protocols = [PROTOCOLS.HTTP, PROTOCOLS.HTTPS],
    unique = true,
    validate = true
  } = options;

  // URL 正则表达式
  const urlRegex = /(https?:\/\/[^\s<>"{}|\\^`[\]]+)/gi;
  const urls = text.match(urlRegex) || [];

  let validUrls = urls;
  
  // 验证 URL
  if (validate) {
    validUrls = urls.filter(url => isValidUrl(url, { protocols }));
  }

  // 去重
  let uniqueUrls = validUrls;
  if (unique) {
    uniqueUrls = [...new Set(validUrls)];
  }

  return {
    urls: validUrls,
    uniqueUrls,
    count: validUrls.length,
    uniqueCount: uniqueUrls.length
  };
}

/**
 * 从文本中提取并去重 URL
 * @param {string} text - 包含 URL 的文本
 * @returns {string[]} 去重后的 URL 数组
 */
function extractUniqueUrls(text) {
  const result = extractUrls(text, { unique: true, validate: true });
  return result.uniqueUrls;
}

/**
 * 解析 URL 参数
 * @param {string} url - 包含参数的 URL
 * @returns {Object} 参数对象
 */
function parseParams(url) {
  try {
    const urlObj = new URL(url);
    const params = {};
    
    urlObj.searchParams.forEach((value, key) => {
      if (params[key]) {
        // 如果已存在，转换为数组
        if (Array.isArray(params[key])) {
          params[key].push(value);
        } else {
          params[key] = [params[key], value];
        }
      } else {
        params[key] = value;
      }
    });
    
    return params;
  } catch {
    return {};
  }
}

/**
 * 构建 URL
 * @param {Object} options - URL 构建选项
 * @returns {string} 构建的 URL
 */
function buildUrl(options = {}) {
  const {
    protocol = PROTOCOLS.HTTPS,
    hostname = 'localhost',
    port = '',
    pathname = '/',
    search = '',
    hash = ''
  } = options;

  let url = `${protocol}//${hostname}`;
  
  if (port) {
    url += `:${port}`;
  }
  
  if (pathname && pathname !== '/') {
    url += pathname.startsWith('/') ? pathname : `/${pathname}`;
  }
  
  if (search) {
    if (typeof search === 'string') {
      url += search.startsWith('?') ? search : `?${search}`;
    } else {
      const searchParams = new URLSearchParams();
      Object.entries(search).forEach(([key, value]) => {
        if (Array.isArray(value)) {
          value.forEach(v => searchParams.append(key, v));
        } else {
          searchParams.set(key, value);
        }
      });
      url += `?${searchParams.toString()}`;
    }
  }
  
  if (hash) {
    url += hash.startsWith('#') ? hash : `#${hash}`;
  }
  
  return url;
}

/**
 * 添加 URL 参数
 * @param {string} url - 原始 URL
 * @param {Object} params - 要添加的参数
 * @returns {string} 新的 URL
 */
function addParams(url, params) {
  try {
    const urlObj = new URL(url);
    
    Object.entries(params).forEach(([key, value]) => {
      if (Array.isArray(value)) {
        value.forEach(v => urlObj.searchParams.append(key, v));
      } else {
        urlObj.searchParams.set(key, value);
      }
    });
    
    return urlObj.toString();
  } catch {
    return url;
  }
}

/**
 * 移除 URL 参数
 * @param {string} url - 原始 URL
 * @param {string[]} paramNames - 要移除的参数名
 * @returns {string} 新的 URL
 */
function removeParams(url, paramNames) {
  try {
    const urlObj = new URL(url);
    
    paramNames.forEach(paramName => {
      urlObj.searchParams.delete(paramName);
    });
    
    return urlObj.toString();
  } catch {
    return url;
  }
}

/**
 * 获取 URL 参数值
 * @param {string} url - URL
 * @param {string} paramName - 参数名
 * @returns {string|null} 参数值
 */
function getParam(url, paramName) {
  try {
    const urlObj = new URL(url);
    return urlObj.searchParams.get(paramName);
  } catch {
    return null;
  }
}

/**
 * 检查是否为 IP 地址
 * @param {string} hostname - 主机名
 * @returns {boolean} 是否为 IP 地址
 */
function isIPAddress(hostname) {
  // IPv4 正则
  const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
  
  // IPv6 正则（简化版）
  const ipv6Regex = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
  
  return ipv4Regex.test(hostname) || ipv6Regex.test(hostname);
}

/**
 * 检查是否为本地 URL
 * @param {string} url - URL
 * @returns {boolean} 是否为本地 URL
 */
function isLocalUrl(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.hostname === 'localhost' || 
           urlObj.hostname === '127.0.0.1' || 
           urlObj.hostname === '0.0.0.0' ||
           urlObj.protocol === 'file:';
  } catch {
    return false;
  }
}

/**
 * 检查是否为 HTTPS URL
 * @param {string} url - URL
 * @returns {boolean} 是否为 HTTPS
 */
function isHttpsUrl(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.protocol === PROTOCOLS.HTTPS;
  } catch {
    return false;
  }
}

/**
 * 检查是否为 HTTP URL
 * @param {string} url - URL
 * @returns {boolean} 是否为 HTTP
 */
function isHttpUrl(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.protocol === PROTOCOLS.HTTP;
  } catch {
    return false;
  }
}

/**
 * 获取域名
 * @param {string} url - URL
 * @returns {string|null} 域名
 */
function getDomain(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.hostname;
  } catch {
    return null;
  }
}

/**
 * 获取协议
 * @param {string} url - URL
 * @returns {string|null} 协议
 */
function getProtocol(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.protocol;
  } catch {
    return null;
  }
}

/**
 * 获取端口
 * @param {string} url - URL
 * @returns {string|null} 端口
 */
function getPort(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.port || getDefaultPort(urlObj.protocol);
  } catch {
    return null;
  }
}

/**
 * 获取默认端口
 * @param {string} protocol - 协议
 * @returns {string} 默认端口
 */
function getDefaultPort(protocol) {
  switch (protocol) {
    case PROTOCOLS.HTTP:
      return '80';
    case PROTOCOLS.HTTPS:
      return '443';
    case PROTOCOLS.FTP:
      return '21';
    case PROTOCOLS.WS:
      return '80';
    case PROTOCOLS.WSS:
      return '443';
    default:
      return '';
  }
}

/**
 * 标准化 URL
 * @param {string} url - 原始 URL
 * @returns {string} 标准化后的 URL
 */
function normalizeUrl(url) {
  try {
    const urlObj = new URL(url);
    
    // 移除默认端口
    if (urlObj.port === getDefaultPort(urlObj.protocol)) {
      urlObj.port = '';
    }
    
    // 标准化路径
    urlObj.pathname = urlObj.pathname.replace(/\/+/g, '/');
    
    return urlObj.toString();
  } catch {
    return url;
  }
}

/**
 * 比较两个 URL 是否相同
 * @param {string} url1 - 第一个 URL
 * @param {string} url2 - 第二个 URL
 * @returns {boolean} 是否相同
 */
function isSameUrl(url1, url2) {
  try {
    const urlObj1 = new URL(url1);
    const urlObj2 = new URL(url2);
    
    return urlObj1.href === urlObj2.href;
  } catch {
    return url1 === url2;
  }
}

/**
 * 检查 URL 是否包含指定域名
 * @param {string} url - URL
 * @param {string} domain - 域名
 * @returns {boolean} 是否包含域名
 */
function containsDomain(url, domain) {
  try {
    const urlObj = new URL(url);
    return urlObj.hostname.includes(domain);
  } catch {
    return false;
  }
}

/**
 * 获取 URL 的哈希值
 * @param {string} url - URL
 * @returns {string|null} 哈希值
 */
function getHash(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.hash;
  } catch {
    return null;
  }
}

/**
 * 设置 URL 的哈希值
 * @param {string} url - 原始 URL
 * @param {string} hash - 新的哈希值
 * @returns {string} 新的 URL
 */
function setHash(url, hash) {
  try {
    const urlObj = new URL(url);
    urlObj.hash = hash;
    return urlObj.toString();
  } catch {
    return url;
  }
}

/**
 * 移除 URL 的哈希值
 * @param {string} url - 原始 URL
 * @returns {string} 新的 URL
 */
function removeHash(url) {
  try {
    const urlObj = new URL(url);
    urlObj.hash = '';
    return urlObj.toString();
  } catch {
    return url;
  }
}

/**
 * 检查 URL 是否安全（HTTPS 或本地）
 * @param {string} url - URL
 * @returns {boolean} 是否安全
 */
function isSecureUrl(url) {
  return isHttpsUrl(url) || isLocalUrl(url);
}

/**
 * 获取 URL 的路径部分
 * @param {string} url - URL
 * @returns {string|null} 路径
 */
function getPath(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.pathname;
  } catch {
    return null;
  }
}

/**
 * 设置 URL 的路径
 * @param {string} url - 原始 URL
 * @param {string} path - 新的路径
 * @returns {string} 新的 URL
 */
function setPath(url, path) {
  try {
    const urlObj = new URL(url);
    urlObj.pathname = path.startsWith('/') ? path : `/${path}`;
    return urlObj.toString();
  } catch {
    return url;
  }
}

/**
 * 检查 URL 是否为相对路径
 * @param {string} url - URL
 * @returns {boolean} 是否为相对路径
 */
function isRelativeUrl(url) {
  return !url.includes('://') && !url.startsWith('//');
}

/**
 * 将相对 URL 转换为绝对 URL
 * @param {string} relativeUrl - 相对 URL
 * @param {string} baseUrl - 基础 URL
 * @returns {string} 绝对 URL
 */
function toAbsoluteUrl(relativeUrl, baseUrl) {
  try {
    return new URL(relativeUrl, baseUrl).toString();
  } catch {
    return relativeUrl;
  }
}

/**
 * 获取 URL 的查询字符串
 * @param {string} url - URL
 * @returns {string|null} 查询字符串
 */
function getQueryString(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.search;
  } catch {
    return null;
  }
}

/**
 * 检查 URL 是否包含查询参数
 * @param {string} url - URL
 * @returns {boolean} 是否包含查询参数
 */
function hasQueryParams(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.searchParams.size > 0;
  } catch {
    return false;
  }
}

/**
 * 获取 URL 的查询参数数量
 * @param {string} url - URL
 * @returns {number} 参数数量
 */
function getQueryParamCount(url) {
  try {
    const urlObj = new URL(url);
    return urlObj.searchParams.size;
  } catch {
    return 0;
  }
}

/**
 * 检查 URL 是否为空
 * @param {string} url - URL
 * @returns {boolean} 是否为空
 */
function isEmptyUrl(url) {
  return !url || url.trim() === '';
}

/**
 * 清理 URL（移除多余的空格和字符）
 * @param {string} url - 原始 URL
 * @returns {string} 清理后的 URL
 */
function cleanUrl(url) {
  if (!url) return '';
  
  return url
    .trim()
    .replace(/\s+/g, '')
    .replace(/[<>"{}|\\^`[\]]/g, '');
}

/**
 * 验证 URL 格式（严格模式）
 * @param {string} url - 要验证的 URL
 * @returns {boolean} 是否有效
 */
function validateUrlStrict(url) {
  if (!url || typeof url !== 'string') {
    return false;
  }

  // 基本格式检查
  const urlPattern = /^https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/;
  
  if (!urlPattern.test(url)) {
    return false;
  }

  // 使用 URL 构造函数进一步验证
  try {
    new URL(url);
    return true;
  } catch {
    return false;
  }
}

/**
 * 批量验证 URL
 * @param {string[]} urls - URL 数组
 * @returns {Object} 验证结果
 */
function validateUrls(urls) {
  const valid = [];
  const invalid = [];

  urls.forEach(url => {
    if (isValidUrl(url)) {
      valid.push(url);
    } else {
      invalid.push(url);
    }
  });

  return {
    valid,
    invalid,
    validCount: valid.length,
    invalidCount: invalid.length
  };
}


// 支持多种导出方式
if (typeof module !== 'undefined' && module.exports) {
  // Node.js 环境
  module.exports = {
    // 函数式导出
    isValidUrl,
    parseUrl,
    extractUrls,
    extractUniqueUrls,
    parseParams,
    buildUrl,
    addParams,
    removeParams,
    getParam,
    isIPAddress,
    isLocalUrl,
    isHttpsUrl,
    isHttpUrl,
    getDomain,
    getProtocol,
    getPort,
    normalizeUrl,
    isSameUrl,
    containsDomain,
    getHash,
    setHash,
    removeHash,
    isSecureUrl,
    getPath,
    setPath,
    isRelativeUrl,
    toAbsoluteUrl,
    getQueryString,
    hasQueryParams,
    getQueryParamCount,
    isEmptyUrl,
    cleanUrl,
    validateUrlStrict,
    validateUrls
  };
} else if (typeof window !== 'undefined') {
  // 浏览器环境
  window.isValidUrl = isValidUrl;
  window.parseUrl = parseUrl;
  window.extractUrls = extractUrls;
  window.extractUniqueUrls = extractUniqueUrls;
  window.parseParams = parseParams;
  window.buildUrl = buildUrl;
  window.addParams = addParams;
  window.removeParams = removeParams;
  window.getParam = getParam;
  window.isIPAddress = isIPAddress;
  window.isLocalUrl = isLocalUrl;
  window.isHttpsUrl = isHttpsUrl;
  window.isHttpUrl = isHttpUrl;
  window.getDomain = getDomain;
  window.getProtocol = getProtocol;
  window.getPort = getPort;
  window.normalizeUrl = normalizeUrl;
  window.isSameUrl = isSameUrl;
  window.containsDomain = containsDomain;
  window.getHash = getHash;
  window.setHash = setHash;
  window.removeHash = removeHash;
  window.isSecureUrl = isSecureUrl;
  window.getPath = getPath;
  window.setPath = setPath;
  window.isRelativeUrl = isRelativeUrl;
  window.toAbsoluteUrl = toAbsoluteUrl;
  window.getQueryString = getQueryString;
  window.hasQueryParams = hasQueryParams;
  window.getQueryParamCount = getQueryParamCount;
  window.isEmptyUrl = isEmptyUrl;
  window.cleanUrl = cleanUrl;
  window.validateUrlStrict = validateUrlStrict;
  window.validateUrls = validateUrls;
}